package org.atlantis.grosbeak.pot.mapper;

import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import org.atlantis.grosbeak.lang.Exceptions;
import org.atlantis.grosbeak.pot.PotException;
import org.atlantis.grosbeak.pot.PotKit;

/**
 * 把数据库中查询出来的数据映射到一个POJO对象中。<br>
 * 注：仅限于POJO对象中的属性名与数据库表中的字段名完全相同。
 * 
 * @author <a href="mailto:seenoevil.cn@gmail.com">SeeNoEvil</a>
 * 
 */
public class PojoRowMapper<T> extends RowMapper<T> {

	protected Class<?> clazz;
	protected Map<String, PropertyMapper> map = new HashMap<String, PropertyMapper>();

	public PojoRowMapper() {
		this.clazz = PotKit.getRowMapperGenericType(getClass());
		Method[] methods = clazz.getMethods();
		for (Method method : methods) {
			PropertyMapper pm = getPropertyMapper(method);
			if (pm != null)
				registerPropertyMapper(pm);
		}
		// TODO 设计一个方法，让用户可以通过继承这个类，实现这个方法而注册更多的映射
	}

	protected final void registerPropertyMapper(PropertyMapper fm) {
		map.put(fm.name, fm);
	}

	protected PropertyMapper getPropertyMapper(Method method) {
		String name = method.getName();
		if (!name.startsWith("set") || name.length() < 4)
			return null;
		Class<?>[] params = method.getParameterTypes();
		if (params.length != 1)
			return null;
		Class<?> param = params[0];
		FieldMapper<?> fm = MapperKit.getFieldMapper(param);
		if (fm == null)
			return null;
		return new PropertyMapper(name.substring(3).toLowerCase(), method,
				!param.isPrimitive(), fm);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.atlantis.grosbeak.pot.mapper.RowMapper#mapRow(java.sql.ResultSet,
	 * java.lang.String[], int[])
	 */
	@SuppressWarnings("unchecked")
	public T mapRow(ResultSet rs, String[] names, int[] types)
			throws SQLException {
		try {
			T pojo = (T) clazz.newInstance();
			for (int i = 0; i < names.length; i++) {
				PropertyMapper pm = map.get(names[i].toLowerCase());
				if (pm != null) {
					Object arg = pm.mapper.mapField(rs, types[i], names[i],
							pm.nullable);
					pm.method.invoke(pojo, arg);
				}
			}
			return pojo;
		} catch (Exception e) {
			throw Exceptions.wrapThrow(PotException.class, e);
		}
	}
}
